package com.movieproject.daos.imp;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.movieproject.daos.MovieDAO;
import com.movieproject.dataobjects.Movie;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.sql.DataSource;


@Service
public class MovieDAOImp implements MovieDAO {
    
	private DataSource dataSource;
	
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	
	public void insert(Movie movie) {
		String sql = "INSERT INTO MOVIES" + "(Title, Actors, Genre, Rating, Description, Year, ImageURL) VALUES (?,?,?,?,?,?,?)";
	
		Connection conn = null;
	
		try {
			conn = dataSource.getConnection();
			PreparedStatement pss = conn.prepareStatement(sql);
			pss.setString(1, movie.getTitle());
			pss.setString(2, movie.getActors());
			pss.setString(3, movie.getGenre());
			pss.setString(4, movie.getRating());
			pss.setString(5, movie.getDescription());
			pss.setString(6, movie.getYear());
			pss.setString(7, movie.getImageURL());
			pss.executeUpdate();
			pss.close();
	
		} catch (SQLException e) {
			throw new RuntimeException(e);
	
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {}
			}
		}
	}

    @Autowired
    private SessionFactory sessionFactory;
    
    

    @Override
    public ArrayList<Movie> getAllMovies() {
	Query query = sessionFactory.getCurrentSession().createQuery("from Movie");
	List<?> l = query.list(); // query returns a raw list. 
	ArrayList<Movie> movieList = new ArrayList<Movie>();
	for(Object o : l) {	// objects in list must be cast individually
	    movieList.add((Movie) o);
	}
	return movieList;
    }

    @Override
    public List<Movie> getMoviesByGenre(String genre) {
	Query query = sessionFactory.getCurrentSession().createQuery("from Movie where genre = :g");
	query.setParameter("g", genre);
	List<?> l = query.list(); // query returns a raw list. 
	ArrayList<Movie> movieList = new ArrayList<Movie>();
	for(Object o : l) {
	    movieList.add((Movie) o);
	}
	return movieList;
    }

    @Override
    public Movie getMovie(int movieID) {
	return (Movie) sessionFactory.getCurrentSession().get(Movie.class, movieID);
    }

    @Override
    public void updateMovie(Movie movie) {
	sessionFactory.getCurrentSession().merge(movie);
    }

    @Override
    public void deleteMovie(Movie movie) {
	sessionFactory.getCurrentSession().delete(movie);
	
    }

    @Override
    public Double getAverageRating(Movie movie) {
	String querystring = "select avg(r.score) from Review r where movieID = :id";
	Query query = sessionFactory.getCurrentSession().createQuery(querystring);
	query.setParameter("id", movie.getMovieID());
	List<?> l = query.list();
	if(l.isEmpty()) return null;
	return (Double) l.get(0);
    }

    @Override
    public Long getPopularity(Movie movie) {
	String querystring = "select count(*) from Order o join o.inventoryItem where movieID = :id";
	Query query = sessionFactory.getCurrentSession().createQuery(querystring);
	query.setParameter("id", movie.getMovieID());
	List<?> l = query.list();
	if(l.isEmpty()) return null;
	return (Long) l.get(0);
    }

	@Override
    public List<Movie> searchMovies(String searchstring) {
		if(searchstring == null || searchstring.equals("")) return new ArrayList<Movie>();
		String[] words = 
				{"a", "an", "and", "are", "as", "at", "be", "but", "by",
				"for", "if", "in", "into", "is", "it",
				"no", "not", "of", "on", "or", "such",
				"that", "the", "their", "then", "there", "these",
				"they", "this", "to", "was", "will", "with"};
		searchstring = searchstring.replaceAll("[^A-Za-z0-9]", " "); //get rid of non alphanumeric characters
		String[] searchStringWords = searchstring.split(" ");
		Set<String> searchWords = new HashSet<String>();
		Set<String> stopWords = new HashSet<String>();
		for(String word : words) stopWords.add(word);
		for(String word : searchStringWords) searchWords.add(word);
		if(stopWords.containsAll(searchWords)) return new ArrayList<Movie>(); //make sure searchstring doesn't consist entirely of stop words
    	int minWordLength = 0;
		for(String word : searchStringWords) {
			if(word.length() > minWordLength) minWordLength = word.length();
		}
		if(minWordLength < 3) return new ArrayList<Movie>();
		Session session = sessionFactory.getCurrentSession();
    	FullTextSession fullTextSession = Search.getFullTextSession(session);
    	QueryBuilder qb = fullTextSession.getSearchFactory()
    		    .buildQueryBuilder().forEntity(Movie.class).get();
    	org.apache.lucene.search.Query query = qb
    			  .keyword()
    			  .onFields("actors", "description", "genre", "title")
    			  .matching(searchstring)
    			  .createQuery();
    	org.hibernate.Query hibQuery = 
    		    fullTextSession.createFullTextQuery(query, Movie.class);
    	
    	List<?> l = hibQuery.list();
    	ArrayList<Movie> result = new ArrayList<Movie>();
        	for(Object o : l) {
        		result.add((Movie) o);
        	}
    	return result;
    	}

	@Override
	public List<String> getGenres() {
		String querystring = "select distinct genre from Movie";
		Query query = sessionFactory.getCurrentSession().createQuery(querystring);
		List<?> l = query.list();
		ArrayList<String> genres = new ArrayList<String>();
		for(Object o : l) {
		    genres.add((String) o);
		}
		return genres;
	}
	public List<String> getRatings() {
	    	String querystring = "select distinct rating from Movie";
		Query query = sessionFactory.getCurrentSession().createQuery(querystring);
		List<?> l = query.list();
		ArrayList<String> ratings = new ArrayList<String>();
		for(Object o : l) {
		    ratings.add((String) o);
		}
		return ratings;
	}

	@Override
	public List<Movie> filterMovies(String genre,
			String year, String rating) {
		String querystring = "from Movie where genre like :g and year like :y and rating like :r";
		Query query = sessionFactory.getCurrentSession().createQuery(querystring);
		if(genre == null || genre.equals("")) genre = "%";
		if(year == null || year.equals("")) year = "%";
		if(rating == null || rating.equals("")) rating = "%";
		query.setParameter("g", genre);
		query.setParameter("y", year);
		query.setParameter("r", rating);
		List<?> l = query.list(); // query returns a raw list. 
		ArrayList<Movie> movieList = new ArrayList<Movie>();
		for(Object o : l) {
		    movieList.add((Movie) o);
		}
		return movieList;
	}
}
